From fb99bde840a30fe4ba431c8cde9b3c8eded7b7f8 Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Fri, 4 Sep 2020 12:51:58 +0100 Subject: [PATCH] Add a secure GtkEntryBuffer MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit We have a widget for password and passphrase entries, but we have no way to handle the data securely. This is usually performed by a separate GtkEntryBuffer—for instance, the one in GCR. While we have API for setting a new entry buffer on GtkText, we don't have API for GtkPasswordEntry, though, so the options are: - expose additional API for GtkPasswordEntry to allow setting a secure text buffer on the internal GtkText widget - provide a secure text buffer out of the box Given that an insecure-by-default GtkPasswordEntry is basically pointless, might as well have a secure buffer built in. We don't really need to make the password entry buffer public out of the box, but we can re-evaluate at a later date. Fixes: #2403 --- gtk/gtkpasswordentrybuffer.c | 196 ++++++++++++++++++++++++++++ gtk/gtkpasswordentrybufferprivate.h | 33 +++++ gtk/meson.build | 1 + 3 files changed, 230 insertions(+) create mode 100644 gtk/gtkpasswordentrybuffer.c create mode 100644 gtk/gtkpasswordentrybufferprivate.h diff --git a/gtk/gtkpasswordentrybuffer.c b/gtk/gtkpasswordentrybuffer.c new file mode 100644 index 0000000000..05844d2fc5 --- /dev/null +++ b/gtk/gtkpasswordentrybuffer.c @@ -0,0 +1,196 @@ +/* gtkpasswordentrybuffer.c: Entry buffer with secure allocation + * + Copyright 2009 Stefan Walter + * Copyright 2020 GNOME Foundation + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#include "config.h" + +#include "gtkpasswordentrybufferprivate.h" + +#include "gtksecurememoryprivate.h" + +#include + +/* Initial size of buffer, in bytes */ +#define MIN_SIZE 16 + +struct _GtkPasswordEntryBuffer +{ + GtkEntryBuffer parent_instance; + + char *text; + gsize text_size; + gsize text_bytes; + guint text_chars; +}; + +G_DEFINE_TYPE (GtkPasswordEntryBuffer, gtk_password_entry_buffer, GTK_TYPE_ENTRY_BUFFER) + +static const char * +gtk_password_entry_buffer_real_get_text (GtkEntryBuffer *buffer, + gsize *n_bytes) +{ + GtkPasswordEntryBuffer *self = GTK_PASSWORD_ENTRY_BUFFER (buffer); + + if (n_bytes != NULL) + *n_bytes = self->text_bytes; + + if (!self->text) + return ""; + + return self->text; +} + +static guint +gtk_password_entry_buffer_real_get_length (GtkEntryBuffer *buffer) +{ + GtkPasswordEntryBuffer *self = GTK_PASSWORD_ENTRY_BUFFER (buffer); + return self->text_chars; +} + +static guint +gtk_password_entry_buffer_real_insert_text (GtkEntryBuffer *buffer, + guint position, + const char *chars, + guint n_chars) +{ + GtkPasswordEntryBuffer *self = GTK_PASSWORD_ENTRY_BUFFER (buffer); + + gsize n_bytes = g_utf8_offset_to_pointer (chars, n_chars) - chars; + + /* Need more memory */ + if (n_bytes + self->text_bytes + 1 > self->text_size) + { + /* Calculate our new buffer size */ + while (n_bytes + self->text_bytes + 1 > self->text_size) + { + if (self->text_size == 0) + { + self->text_size = MIN_SIZE; + } + else + { + if (2 * self->text_size < GTK_ENTRY_BUFFER_MAX_SIZE) + { + self->text_size *= 2; + } + else + { + self->text_size = GTK_ENTRY_BUFFER_MAX_SIZE; + if (n_bytes > self->text_size - self->text_bytes - 1) + { + n_bytes = self->text_size - self->text_bytes - 1; + n_bytes = g_utf8_find_prev_char (chars, chars + n_bytes + 1) - chars; + n_chars = g_utf8_strlen (chars, n_bytes); + } + break; + } + } + } + + self->text = gtk_secure_realloc (self->text, self->text_size); + } + + /* Actual text insertion */ + gsize at = g_utf8_offset_to_pointer (self->text, position) - self->text; + memmove (self->text + at + n_bytes, self->text + at, self->text_bytes - at); + memcpy (self->text + at, chars, n_bytes); + + /* Book keeping */ + self->text_bytes += n_bytes; + self->text_chars += n_chars; + self->text[self->text_bytes] = '\0'; + + gtk_entry_buffer_emit_inserted_text (buffer, position, chars, n_chars); + + return n_chars; +} + +static guint +gtk_password_entry_buffer_real_delete_text (GtkEntryBuffer *buffer, + guint position, + guint n_chars) +{ + GtkPasswordEntryBuffer *self = GTK_PASSWORD_ENTRY_BUFFER (buffer); + + if (position > self->text_chars) + position = self->text_chars; + if (position + n_chars > self->text_chars) + n_chars = self->text_chars - position; + + if (n_chars > 0) + { + gsize start = g_utf8_offset_to_pointer (self->text, position) - self->text; + gsize end = g_utf8_offset_to_pointer (self->text, position + n_chars) - self->text; + + memmove (self->text + start, self->text + end, self->text_bytes + 1 - end); + self->text_chars -= n_chars; + self->text_bytes -= (end - start); + + gtk_entry_buffer_emit_deleted_text (buffer, position, n_chars); + } + + return n_chars; +} + +static void +gtk_password_entry_buffer_finalize (GObject *gobject) +{ + GtkPasswordEntryBuffer *self = GTK_PASSWORD_ENTRY_BUFFER (gobject); + + g_clear_pointer (&self->text, gtk_secure_free); + + self->text_bytes = 0; + self->text_size = 0; + self->text_chars = 0; + + G_OBJECT_CLASS (gtk_password_entry_buffer_parent_class)->finalize (gobject); +} + +static void +gtk_password_entry_buffer_class_init (GtkPasswordEntryBufferClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GtkEntryBufferClass *buffer_class = GTK_ENTRY_BUFFER_CLASS (klass); + + gobject_class->finalize = gtk_password_entry_buffer_finalize; + + buffer_class->get_text = gtk_password_entry_buffer_real_get_text; + buffer_class->get_length = gtk_password_entry_buffer_real_get_length; + buffer_class->insert_text = gtk_password_entry_buffer_real_insert_text; + buffer_class->delete_text = gtk_password_entry_buffer_real_delete_text; +} + +static void +gtk_password_entry_buffer_init (GtkPasswordEntryBuffer *self) +{ +} + +/*< private > + * gtk_password_entry_buffer_new: + * + * Creates a new #GtkEntryBuffer using secure memory allocations. + * + * Returns: (transfer full): the newly created instance + */ +GtkEntryBuffer * +gtk_password_entry_buffer_new (void) +{ + return g_object_new (GTK_TYPE_PASSWORD_ENTRY_BUFFER, NULL); +} diff --git a/gtk/gtkpasswordentrybufferprivate.h b/gtk/gtkpasswordentrybufferprivate.h new file mode 100644 index 0000000000..2f6f1da2cd --- /dev/null +++ b/gtk/gtkpasswordentrybufferprivate.h @@ -0,0 +1,33 @@ +/* gtkpasswordentrybufferprivate.h: Entry buffer using secure allocation + * + * Copyright 2020 GNOME Foundation + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#pragma once + +#include + +G_BEGIN_DECLS + +#define GTK_TYPE_PASSWORD_ENTRY_BUFFER (gtk_password_entry_buffer_get_type()) + +G_DECLARE_FINAL_TYPE (GtkPasswordEntryBuffer, gtk_password_entry_buffer, GTK, PASSWORD_ENTRY_BUFFER, GtkEntryBuffer) + +GtkEntryBuffer * gtk_password_entry_buffer_new (void); + +G_END_DECLS diff --git a/gtk/meson.build b/gtk/meson.build index c644ac9696..eed4dd734b 100644 --- a/gtk/meson.build +++ b/gtk/meson.build @@ -121,6 +121,7 @@ gtk_private_sources = files([ 'gtkmenutrackeritem.c', 'gtkpango.c', 'gskpango.c', + 'gtkpasswordentrybuffer.c', 'gtkpathbar.c', 'gtkplacessidebar.c', 'gtkplacesview.c', -- 2.30.2